package edu.spbsu.nonsm00th.computgeom.sort.impl;

import edu.spbsu.nonsm00th.algorithms.sort.Comparator;
import edu.spbsu.nonsm00th.model.Point;
import edu.spbsu.nonsm00th.model.enums.Ratio;
import edu.spbsu.nonsm00th.util.Precondition;

/**
 * Polar comparator realization. Polar coordinates lexicographic comparator.
 * Should be used only for 2-dimensional <code>Point</code>s.
 *
 * @author mbuzdov
 */
public class ComparatorD2Polar implements Comparator<Point> {

    private Point origin;

    public ComparatorD2Polar(Point origin) {
        this.origin = origin;
    }

    public Ratio compare(final Point a, final Point b) {
        Precondition.argsNotNull(a, b);

        double ratingA = angleRating(a);
        double ratingB = angleRating(b);
        if ( ratingA > ratingB ) return Ratio.GREATER;
        if ( ratingA < ratingB ) return Ratio.LESS;
        //ratingA == ratingB
        double distanceA =
                (a.getX() - origin.getX()) * (a.getX() - origin.getX()) +
                (a.getY() - origin.getY()) * (a.getY() - origin.getY());
        double distanceB =
                (b.getX() - origin.getX()) * (b.getX() - origin.getX()) +
                (b.getY() - origin.getY()) * (b.getY() - origin.getY());
        if ( 0 == ratingA || 1 == ratingA || 2 == ratingA || 3 == ratingA || 4 == ratingA ) {
            if ( distanceA > distanceB ) return Ratio.GREATER;
            if ( distanceA < distanceB ) return Ratio.LESS;
            return Ratio.EQUAL;
        }
        //x != 0 && y != 0
        double tgA = (a.getY() - origin.getY()) / (a.getX() - origin.getX());
        double tgB = (b.getY() - origin.getY()) / (b.getX() - origin.getX());
        if ( tgA > tgB ) return Ratio.GREATER;
        if ( tgA < tgB ) return Ratio.LESS;
        //tgA == tgB
        if ( distanceA > distanceB ) return Ratio.GREATER;
        if ( distanceA < distanceB ) return Ratio.LESS;
        return Ratio.EQUAL;
    };

    private double angleRating(final Point p) {
        if ( origin.getX() == p.getX() && origin.getY() == p.getY() ) {
            return 0;
        }
        if ( origin.getX() == p.getX() ) {
            return (p.getY() > origin.getY() ? 3 : 1);
        }
        if ( origin.getY() == p.getY() ) {
            return (p.getX() > origin.getX() ? 2 : 4);
        }

        if ( origin.getX() < p.getX() && origin.getY() > p.getY() ) {
            return 1.5;
        }
        if ( origin.getX() < p.getX() && origin.getY() < p.getY() ) {
            return 2.5;
        }
        if ( origin.getX() > p.getX() && origin.getY() < p.getY() ) {
            return 3.5;
        }
        //x<0 && y<0
        return 4.5;
    }

}
